package com.mlethe.library.utils.file;

import android.content.ContentResolver;
import android.content.Context;
import android.content.res.AssetManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.MediaStore;
import android.webkit.MimeTypeMap;
import android.widget.TextView;

import androidx.annotation.RawRes;
import androidx.core.content.FileProvider;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

/**
 * 文件操作工具类
 *
 * @author Mlethe
 */
public class FileUtil {

    private Context mContext;

    private FileUtil() {
    }

    private static final class Holder {
        private static final FileUtil INSTANCE = new FileUtil();
    }

    /**
     * 初始化
     *
     * @param context
     */
    public static void init(Context context) {
        getInstance().mContext = context.getApplicationContext();
    }

    private static FileUtil getInstance() {
        return Holder.INSTANCE;
    }

    /**
     * 获取APP公共文件路径
     * Android 29 弃用 Environment.getExternalStorageDirectory() 方法
     * Android 8 添加 Context.getExternalFilesDir()方法
     *
     * @return path = /storage/emulated/0/Android/data/包名/files/
     */
    public static String getExternalFilesDir() {
        Context context = getInstance().mContext;
        if (context == null) {
            return null;
        }
        File filesDir = context.getExternalCacheDir();
        if (filesDir == null) {
            return null;
        }
        return filesDir.getParent() + "/";
    }

    /**
     * 获取APP公共文件路径
     * Android 29 弃用 Environment.getExternalStorageDirectory() 方法
     * Android 8 添加 Context.getExternalFilesDir()方法
     *
     * @param type    返回的文件目录的类型
     *                文件目录:
     *                {@link android.os.Environment#DIRECTORY_MUSIC},
     *                {@link android.os.Environment#DIRECTORY_PODCASTS},
     *                {@link android.os.Environment#DIRECTORY_RINGTONES},
     *                {@link android.os.Environment#DIRECTORY_ALARMS},
     *                {@link android.os.Environment#DIRECTORY_NOTIFICATIONS},
     *                {@link android.os.Environment#DIRECTORY_PICTURES}, or
     *                {@link android.os.Environment#DIRECTORY_MOVIES}.
     * @return
     */
    public static String getExternalFilesDir(String type) {
        Context context = getInstance().mContext;
        if (context == null) {
            return null;
        }
        File filesDir = context.getExternalFilesDir(type);
        if (filesDir == null) {
            return null;
        }
        return filesDir.getPath() + "/";
    }

    /**
     * 创建缓存存储路径
     * Android 29 弃用 Environment.getExternalStorageDirectory() 方法
     * Android 8 添加 Context.getExternalFilesDir()方法
     *
     * @return path = /storage/emulated/0/Android/data/包名/cache/
     */
    public static String getCacheDirectory() {
        Context context = getInstance().mContext;
        if (context == null) {
            return null;
        }
        File cacheDir = context.getExternalCacheDir();
        if (cacheDir == null) {
            return null;
        }
        return cacheDir.getPath() + "/";
    }

    /**
     * 创建文件夹
     *
     * @param dirPath 文件夹路径
     * @return
     */
    public static File createDir(String dirPath) {
        final File fileDir = new File(dirPath);
        if (!fileDir.exists()) {
            boolean flag = fileDir.mkdirs();
            if (!flag) {
                return null;
            }
        }
        return fileDir;
    }

    /**
     * 创建文件
     *
     * @param dirPath  文件夹路径
     * @param fileName 文件名称
     * @return
     */
    public static File createFile(String dirPath, String fileName) {
        File dirFile = createDir(dirPath);
        if (dirFile == null) {
            return null;
        }
        File file = new File(dirFile, fileName);
        if (!file.exists()) {
            try {
                boolean flag = file.createNewFile();
                if (!flag) {
                    return null;
                }
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            }
        }
        return file;
    }

    /**
     * 创建文件
     *
     * @param dirName  文件夹名称
     * @param fileName 文件名称
     * @return
     */
    public static File createCacheFile(String dirName, String fileName) {
        /*
         * 拼接成SD卡中完整的dir
         */
        String dirPath = getCacheDirectory();
        if (dirPath == null) {
            return null;
        }
        if (dirName != null && !"".equals(dirName)) {
            dirPath += dirName + "/";
        }
        return createFile(dirPath, fileName);
    }

    /**
     * 创建文件
     *
     * @param fileName 文件名称
     * @return
     */
    public static File createCacheFile(String fileName) {
        return createCacheFile(null, fileName);
    }

    /**
     * 删除文件（若为目录，则递归删除子目录和文件）
     *
     * @param file 文件
     */
    public static boolean deleteFile(File file) {
        if (file == null || !file.exists()) {
            return false;
        }
        if (file.isDirectory()) {
            File[] subFiles = file.listFiles();
            if (subFiles != null) {
                boolean flag = true;
                // 删除子目录和文件
                for (File subFile : subFiles) {
                    if (!deleteFile(subFile)) {
                        flag = false;
                    }
                }
                if (flag) {
                    return file.delete();
                }
                return false;
            }
        }
        return file.delete();
    }

    /**
     * 获取文件的后缀名
     *
     * @param filePath 文件路径
     * @return
     */
    public static String getExtension(String filePath) {
        String suffix = "";
        final File file = new File(filePath);
        final String name = file.getName();
        final int idx = name.lastIndexOf('.');
        if (idx > 0) {
            suffix = name.substring(idx + 1);
        }
        return suffix;
    }

    /**
     * 获取文件的MIME
     *
     * @param filePath 文件路径
     * @return
     */
    public static String getMimeType(String filePath) {
        final String extension = getExtension(filePath);
        return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
    }

    /**
     * 检查是否存在SD卡
     *
     * @return true 有，false 无
     */
    public static boolean hasSdcard() {
        return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
    }

    /**
     * @param path
     * @param textView
     */
    public static void setIconFont(String path, TextView textView) {
        final Typeface typeface = Typeface.createFromAsset(textView.getContext().getApplicationContext().getAssets(), path);
        textView.setTypeface(typeface);
    }

    /**
     * 字符串写入文件
     *
     * @param content 内容
     * @param file    文件
     * @param append  true表示不覆盖原来的内容，而是加到文件的后面。若要覆盖原来的内容，直接省略这个参数就好
     */
    public static void write(String content, File file, boolean append) {
        if (file == null || !file.exists()) {
            return;
        }
        FileWriter fw = null;
        try {
            fw = new FileWriter(file, append);
            fw.write(content);
            fw.flush();
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            try {
                if (fw != null) {
                    fw.close();
                    fw = null;
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }

    /**
     * 字符串写入文件
     *
     * @param content 内容
     * @param file    文件
     */
    public static void write(String content, File file) {
        write(content, file, false);
    }

    /**
     * 字符串写入文件
     *
     * @param content  内容
     * @param dirPath  文件夹路径
     * @param fileName 文件名称
     */
    public static void write(String content, String dirPath, String fileName) {
        File file = createFile(dirPath, fileName);
        write(content, file);
    }

    /**
     * 写入文件
     *
     * @param is   输入流
     * @param file 输出文件
     */
    public static void write(InputStream is, File file) {
        if (file == null || !file.exists()) {
            return;
        }
        BufferedInputStream bis = null;
        FileOutputStream fos = null;
        BufferedOutputStream bos = null;
        try {
            bis = new BufferedInputStream(is);
            fos = new FileOutputStream(file);
            bos = new BufferedOutputStream(fos);
            byte[] data = new byte[1024 * 1024];
            int count;
            while ((count = bis.read(data)) != -1) {
                bos.write(data, 0, count);
            }
            bos.flush();
            fos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (bos != null) {
                    bos.close();
                    bos = null;
                }
                if (fos != null) {
                    fos.close();
                    fos = null;
                }
                if (bis != null) {
                    bis.close();
                    bis = null;
                }
                if (is != null) {
                    is.close();
                    is = null;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 写入文件
     *
     * @param is       输入流
     * @param dirPath  输出文件的文件夹路径
     * @param fileName 输出文件的名称
     * @return
     */
    public static File write(InputStream is, String dirPath, String fileName) {
        File file = createFile(dirPath, fileName);
        write(is, file);
        return file;
    }

    /**
     * 写入文件
     *
     * @param is       输入流
     * @param fileName 输出文件名称
     * @return
     */
    public File write(InputStream is, String fileName) {
        File file = createCacheFile(fileName);
        write(is, file);
        return file;
    }

    /**
     * 异常写入文件
     *
     * @param throwable 异常
     * @param dirPath   输出文件的文件夹路径
     * @param fileName  输出文件的名称
     * @return
     */
    public static File write(Throwable throwable, String dirPath, String fileName) {
        File file = createFile(dirPath, fileName);
        String exceptionStr = getExceptionInfo(throwable);
        if (exceptionStr == null) {
            return null;
        }
        ByteArrayInputStream inputStream = new ByteArrayInputStream(exceptionStr.getBytes());
        write(inputStream, file);
        return file;
    }

    /**
     * 异常写入文件
     *
     * @param throwable 异常
     * @param fileName  输出文件的名称
     * @return
     */
    public File write(Throwable throwable, String fileName) {
        File file = createCacheFile(fileName);
        String exceptionStr = getExceptionInfo(throwable);
        if (exceptionStr == null) {
            return null;
        }
        ByteArrayInputStream inputStream = new ByteArrayInputStream(exceptionStr.getBytes());
        write(inputStream, file);
        return file;
    }

    /**
     * 获取错误的信息
     *
     * @param throwable
     * @return
     */
    private static String getExceptionInfo(final Throwable throwable) {
        PrintWriter pw = null;
        Writer writer = null;
        try {
            writer = new StringWriter();
            pw = new PrintWriter(writer);
            throwable.printStackTrace(pw);
            return writer.toString();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (pw != null) {
                    pw.close();
                    pw = null;
                }
                if (writer != null) {
                    writer.close();
                    writer = null;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 读取assets目录下的字符串文件
     *
     * @param context  上下文
     * @param filePath assets中的文件路径
     * @return 字符串
     */
    public static String getAssetsFile(Context context, String filePath) {
        InputStream is = null;
        BufferedInputStream bis = null;
        InputStreamReader isr = null;
        BufferedReader br = null;
        StringBuilder stringBuilder = null;
        final AssetManager assetManager = context.getApplicationContext().getAssets();
        try {
            is = assetManager.open(filePath);
            bis = new BufferedInputStream(is);
            isr = new InputStreamReader(bis);
            br = new BufferedReader(isr);
            stringBuilder = new StringBuilder();
            String str;
            while ((str = br.readLine()) != null) {
                stringBuilder.append(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (br != null) {
                    br.close();
                }
                if (isr != null) {
                    isr.close();
                }
                if (bis != null) {
                    bis.close();
                }
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (stringBuilder != null) {
            return stringBuilder.toString();
        } else {
            return null;
        }
    }

    /**
     * 复制assets文件
     *
     * @param assetsFilePath assets中的文件路径
     * @param dirName        文件夹名称
     * @param fileName       文件名
     * @return
     */
    public static File copyAssetsFile(String assetsFilePath, String dirName, String fileName) {
        InputStream is = null;
        OutputStream os = null;
        try {
            File file = createFile(getExternalFilesDir() + dirName, fileName);
            if (file == null) {
                return null;
            }
            final AssetManager assetManager = getInstance().mContext.getAssets();
            is = assetManager.open(assetsFilePath);
            os = new FileOutputStream(file);
            byte[] bytes = new byte[1024 * 1024];
            int length;
            while ((length = is.read(bytes)) != -1) {
                os.write(bytes, 0, length);
            }
            os.flush();
            return file;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (os != null) {
                    os.close();
                    os = null;
                }
                if (is != null) {
                    is.close();
                    is = null;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 读取raw目录下的字符串文件
     *
     * @param context 上下文
     * @param rawId   资源id
     * @return
     */
    public static String getRawFile(Context context, @RawRes int rawId) {
        InputStream is = null;
        BufferedInputStream bis = null;
        InputStreamReader isr = null;
        BufferedReader br = null;
        StringBuilder stringBuilder = new StringBuilder();
        try {
            is = context.getApplicationContext().getResources().openRawResource(rawId);
            bis = new BufferedInputStream(is);
            isr = new InputStreamReader(bis);
            br = new BufferedReader(isr);
            String str;
            while ((str = br.readLine()) != null) {
                stringBuilder.append(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (br != null) {
                    br.close();
                }
                if (isr != null) {
                    isr.close();
                }
                if (bis != null) {
                    bis.close();
                }
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return stringBuilder.toString();
    }

    /**
     * 保存bitmap
     *
     * @param bitmap   图像
     * @param compress 压缩比例 100是不压缩,值约小压缩率越高
     * @return
     */
    public File saveBitmap(Bitmap bitmap, int compress) {
        if (bitmap == null) {
            return null;
        }
        FileOutputStream fos = null;
        BufferedOutputStream bos = null;
        String dir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
        if (dir == null || "".equals(dir)) {
            return null;
        }
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss_SSS", Locale.getDefault());
        File file = createFile(dir, dateFormat.format(new Date()) + ".jpg");
        try {
            fos = new FileOutputStream(file);
            bos = new BufferedOutputStream(fos);
            // 把数据写入文件
            bitmap.compress(Bitmap.CompressFormat.JPEG, compress, bos);
            bos.flush();
            fos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (bos != null) {
                    bos.close();
                    bos = null;
                }
                if (fos != null) {
                    fos.close();
                    fos = null;
                }
                bitmap.recycle();
                bitmap = null;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return file;
    }

    /**
     * 获得文件名
     *
     * @param filePath url 或 文件路径
     * @return 文件名称
     */
    public static String getFileName(String filePath) {
        if (filePath == null || filePath.length() == 0) {
            return null;
        }
        int nameStart = filePath.lastIndexOf('/') + 1;
        return filePath.substring(nameStart);
    }

    /**
     * 获取文件真实路径
     *
     * @param context 上下文
     * @param uri     uri
     * @return
     */
    @SuppressWarnings("deprecation")
    public static String getFilePath(final Context context, final Uri uri) {
        if (null == uri) {
            return null;
        }
        final String scheme = uri.getScheme();
        String data = null;
        if (scheme == null) {
            data = uri.getPath();
        } else if (ContentResolver.SCHEME_FILE.equals(scheme)) {
            data = uri.getPath();
        } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {
            final Cursor cursor = context.getContentResolver().query(uri, new String[]{MediaStore.Images.ImageColumns.DATA}, null, null, null);
            if (null != cursor) {
                if (cursor.moveToFirst()) {
                    final int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
                    if (index > -1) {
                        data = cursor.getString(index);
                    }
                }
                cursor.close();
            }
        }
        return data;
    }

    /**
     * 将文件转换成uri(支持7.0)
     *
     * @param context 上下文
     * @param file    文件
     * @return
     */
    public static Uri getUriForFile(Context context, File file) {
        Uri fileUri = null;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            fileUri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", file);
        } else {
            fileUri = Uri.fromFile(file);
        }
        return fileUri;
    }

    /**
     * 获取文件大小，单位为byte（若为目录，则包括所有子目录和文件）
     *
     * @param file 文件
     * @return
     */
    public static long getFileSize(File file) {
        long size = 0;
        if (file.exists()) {
            if (file.isDirectory()) {
                File[] subFiles = file.listFiles();
                if (subFiles != null) {
                    int num = subFiles.length;
                    for (int i = 0; i < num; i++) {
                        size += getFileSize(subFiles[i]);
                    }
                }
            } else {
                size += file.length();
            }
        }
        return size;
    }

    /**
     * 文件大小转换
     *
     * @param size 文件大小
     * @return
     */
    public static String getPrintSize(long size) {
        //如果字节数少于1024，则直接以B为单位，否则先除于1024，后3位因太少无意义
        if (size < 1024) {
            return size + "B";
        } else {
            size = size / 1024;
        }
        //如果原字节数除于1024之后，少于1024，则可以直接以KB作为单位
        //因为还没有到达要使用另一个单位的时候
        //接下去以此类推
        if (size < 1024) {
            return size + "KB";
        } else {
            size = size / 1024;
        }
        if (size < 1024) {
            //因为如果以MB为单位的话，要保留最后1位小数，
            //因此，把此数乘以100之后再取余
            size = size * 100;
            return (size / 100) + "." + (size % 100) + "MB";
        } else {
            //否则如果要以GB为单位的，先除于1024再作同样的处理
            size = size * 100 / 1024;
            return (size / 100) + "." + (size % 100) + "GB";
        }
    }
}
